//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Graphics/Tools/ParticleSystem.h"

namespace Engine
{
namespace Graphics
{


	//
	// Particle Linear Emitter
	//

	template <typename LineType>
	struct ParticleLineEmitter
	{
	// types
	public:
		typedef LineType							line_t;
		typedef typename LineType::vec_t			vec_t;
		typedef ParticleLineEmitter< LineType >		Self;


	// variables
	private:
		LineType	_line;
		vec_t		_direction;


	// methods
	public:
		ParticleLineEmitter ()
		{
			_direction.x = 1.0f;
		}

		explicit
		ParticleLineEmitter (const line_t &line, const vec_t &dir) : _line(line), _direction(dir)
		{}


		void GetParticleData (float interpolation, OUT vec_t &pos, OUT vec_t &dir) const
		{
			pos.InterpolateL( _line.Begin(), _line.End(), interpolation );
			dir = _direction;
		}
	};



	//
	// Particle Point Emitter
	//

	struct ParticlePointEmitter
	{
	// types
	public:
		typedef TCircle< float >		circle_t;
		typedef circle_t::vec_t			vec_t;
		typedef ParticlePointEmitter	Self;


	// variables
	private:
		circle_t	_circle;
		float		_startAngle,
					_endAngle;


	// methods
	public:
		ParticlePointEmitter () : _startAngle(0.0f), _endAngle(0.0f)
		{}

		explicit
		ParticlePointEmitter (const vec_t &pos, float startOffset, float startAngleRad, float endAngleRad) :
			_circle( pos, startOffset ), _startAngle(startAngleRad), _endAngle(endAngleRad)
		{}

		explicit
		ParticlePointEmitter (const vec_t &pos, float startOffset, const vec_t &startDirection, const vec_t &endDirection) :
			_circle( pos, startOffset )
		{
			_startAngle	= _circle.GetAngle( startDirection );
			_endAngle	= _circle.GetAngle( endDirection );
		}
		

		void GetParticleData (float interpolation, OUT vec_t &pos, OUT vec_t &dir) const
		{
			pos = _circle.GetPoint( Mix( _startAngle, _endAngle, interpolation ) );
			dir = ( pos - _circle.Center() ).Normalized();
		}
	};



	//
	// Particle Circle Emitter
	//

	struct ParticleCircleEmitter
	{
	// types
	public:
		typedef TCircle< float >			circle_t;
		typedef circle_t::vec_t				vec_t;
		typedef ParticleCircleEmitter		Self;


	// variables
	private:
		circle_t	_circle;


	// methods
	public:
		ParticleCircleEmitter ()
		{}

		explicit
		ParticleCircleEmitter (const vec_t &center, float radius) : _circle( center, radius )
		{}
		

		void GetParticleData (float interpolation, OUT vec_t &pos, OUT vec_t &dir) const
		{
			pos = _circle.GetPoint( interpolation * 2.0f * Pi<float>() );
			dir = ( pos - _circle.Center() ).Normalized();
		}
	};



	//
	// Particle Random Velocity
	//

	template <typename VecType, uint Count, typename E = DefaultRandomEngine>
	struct ParticleRandomVelocity
	{
	// types
	public:
		enum {
			COUNT	= 1 << StaticPOT< Count >::value,
			MASK	= COUNT - 1,
		};
		typedef typename VecType::value_t					value_t;
		typedef VecType										vec_t;
		typedef ParticleRandomVelocity< VecType, Count >	Self;
		typedef static_array< vec_t, COUNT >				vec_array_t;


	// variables
	private:
		vec_array_t		_rndVelocities;


	// methods
	public:
		ParticleRandomVelocity ()
		{}

		void Generate (const vec_t &delta)
		{
			FOR( i, _rndVelocities )
			{
				const usize	j = COUNT-i-1;
				_rndVelocities[i][0] = Random::SNorm< value_t >() * delta.x;
				_rndVelocities[j][1] = Random::SNorm< value_t >() * delta.y;
			}
		}

		void Add (INOUT vec_t &velocity, uint index, float scale) const
		{
			velocity += _rndVelocities[ index & MASK ] * scale;
		}

		void Init (INOUT vec_t &velocity, uint index, float scale) const
		{
			vec_t	normal_vec = velocity * scale;

			velocity += _rndVelocities[ index & MASK ];
			velocity *= scale;
		}
	};



	//
	// Particle Emitter Distribution Linear Fetch
	//

	template <uint Count>
	struct ParticleEmitterDistribLinearFetch
	{
	// variables
	private:
		uint	_counter;


	// methods
	public:
		ParticleEmitterDistribLinearFetch () : _counter(0)
		{}

		uint GetIndex ()
		{
			uint	res = _counter;

			++_counter;

			if ( _counter >= Count )
				_counter = 0;

			return res;
		}
	};



	//
	// Particle Emitter Distribution Random Fetch
	//

	template <uint Count>
	struct ParticleEmitterDistribRandomFetch
	{
	// methods
	public:
		ParticleEmitterDistribRandomFetch ()
		{}

		uint GetIndex ()
		{
			return uint( Random::UNorm<float>() * (Count-1) + 0.5f );
		}
	};



	//
	// Particle Emitter Distribution
	//

	template <typename EmitterShape, uint Count,
			  template <uint> class Fetch = ParticleEmitterDistribLinearFetch
			 >
	struct ParticleEmitterDistribution
	{
	// types
	public:
		enum {
			COUNT	= Count & ~1
		};

		typedef ParticleEmitterDistribution< EmitterShape, Count, Fetch >	Self;
		typedef EmitterShape												shape_t;
		typedef typename shape_t::vec_t										vec_t;
		typedef Fetch< COUNT >												fetcher_t;

	private:
		struct _Data
		{
			vec_t	pos;
			vec_t	dir;
			float	posInterp;
			float	velInterp;
			///
			_Data () : posInterp(0.0f), velInterp(0.0f) {}

			vec_t Velocity () const		{ return dir * velInterp; }
		};

		typedef static_array< _Data, COUNT >	particle_starts_t;


	// variables
	private:
		shape_t				_shape;
		particle_starts_t	_starts;
		fetcher_t			_fetcher;
		uint				_counter;


	// methods
	public:
		ParticleEmitterDistribution () : _counter(0)
		{}

		explicit
		ParticleEmitterDistribution (const shape_t &shape) : _shape(shape), _counter(0)
		{}


		void GetNextData (OUT vec_t &pos, OUT vec_t & dir)
		{
			_counter = _fetcher.GetIndex();

			pos = _starts[ _counter ].pos;
			dir = _starts[ _counter ].Velocity();
		}

		uint GetCounter () const
		{
			return _counter;
		}


		// Linear
		void GenLinearPositins ()
		{
			FOR( i, _starts )
			{
				_shape.GetParticleData( _starts[i].posInterp = _GetInterp( i ), _starts[i].pos, _starts[i].dir );
			}
		}

		void GenLinearVelocities ()
		{
			FOR( i, _starts ) {
				_starts[i].velInterp = 1.0f;
			}
		}


		// Exponential
		void GenExponentialPositions (float value /*1..10*/, float center /*0..1*/)
		{
			struct Util {
				static float Get (float x, float k, float center) {
					const float	s = x < center ? -1.0f : 1.0f;
					return Pow( (x - center) * s * 2.0f, k ) * s * 0.5f + 0.5f;
				}
			};
			
			FOR( i, _starts )
			{
				_shape.GetParticleData(
					_starts[i].posInterp = Util::Get( _GetInterp( i ), value, center ),
					_starts[i].pos,
					_starts[i].dir );
			}
		}
		
		void GenExponentialVelocities (float value /*1..10*/, float center /*0..1*/)
		{
			struct Util {
				static float Get (float x, float k, float center) {
					return 1.0f / ( 1.0f + Square( (x - center) * k ) );
				}
			};

			FOR( i, _starts )
			{
				_starts[i].velInterp = Util::Get( _GetInterp( i ), value, center );
			}
		}


	private:
		static float _GetInterp (usize i)
		{
			return float(i) / (COUNT - 1);
			//return float( IsEven( i ) ? (i>>1) : (COUNT - 1 - (i>>1)) ) / (COUNT - 1);
		}
	};


}	// Graphics
}	// Engine